package bloque;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;


import bloque.*;

import bloque.RegistroHoja;

public class HojaArbol {
	
	private List<RegistroHoja> listaRegistroHoja;
	private Integer punteroASiguiente;


	public HojaArbol() {
		listaRegistroHoja = new ArrayList<RegistroHoja>();
	}

	public HojaArbol(List<RegistroHoja> listaRegistroTermino,Integer posicionEnArchivo, Integer punteroASiguiente) {
		this.listaRegistroHoja = listaRegistroTermino;
		this.punteroASiguiente = punteroASiguiente;
	}

	public void convertirBytesANodo(byte[] xInput) throws IOException {

		ByteArrayInputStream bytesRegistro = new ByteArrayInputStream(xInput);
		DataInputStream input = new DataInputStream(bytesRegistro);
				
		listaRegistroHoja = new ArrayList<RegistroHoja>();
		int tamanioListaRegistroHoja = input.readInt();
		for(int i=0;i<tamanioListaRegistroHoja;i++) {
			int tamanioRegistroHoja=input.readInt();
			byte[] bytesRegistroHoja= new byte[tamanioRegistroHoja];
			input.read(bytesRegistroHoja);
			RegistroHoja registroHojaAuxiliar = new RegistroHoja();
			registroHojaAuxiliar.convertirBytesARegistro(bytesRegistroHoja);
			listaRegistroHoja.add(i, registroHojaAuxiliar);
		}
		punteroASiguiente=input.readInt();

	}
	
	public byte[] convertirNodoABytes() throws IOException {
				
		ByteArrayOutputStream bytesNodo = new ByteArrayOutputStream();
	    DataOutputStream output = new DataOutputStream(bytesNodo);
					     
		output.writeInt(listaRegistroHoja.size());
		for(int i=0; i<listaRegistroHoja.size();i++) {
			output.writeInt(listaRegistroHoja.get(i).convertirRegistroABytes().length);
			output.write(listaRegistroHoja.get(i).convertirRegistroABytes());
		}		
		output.writeInt(punteroASiguiente);
		
		return bytesNodo.toByteArray();
		
	}
	
	public List<RegistroHoja> getListaRegistroTermino() {
		return this.listaRegistroHoja;
	}
	public Integer getPunteroASiguiente() {
		return this.punteroASiguiente;
	}

	/**@return Metodo void, inserta el registro si no existe o actualiza el offset y el peso global
	 * 
	 * @param String: palabra a insertar; 
	 *        int: offset a la lista de documentos; 
	 *        float: peso global de la palabra; 
	 */
	
	public void insertar(String palabra, int offset, float pesoGlobal) {
		if (this.buscar(palabra)==false){
			RegistroHoja reg = new RegistroHoja();
			RegistroHoja reg2 = new RegistroHoja();
			String str;
			RegistroHoja[] listaAux = new RegistroHoja[listaRegistroHoja.size()+1];
			reg.setCantCaractNuevos(palabra.length());
			reg.setCantCaractRepetidos(0);
			reg.setOffset(offset);
			reg.setPesoGlobal(pesoGlobal);
			reg.setTerm(palabra);
			listaAux[listaRegistroHoja.size()] = reg;
			
			//cargo los registros de a uno en la lista auxilar, con los terminos expandidos
			if (listaRegistroHoja.isEmpty()==false){
				for (int i = 0; i < listaRegistroHoja.size(); i++) {
					reg=listaRegistroHoja.get(i);
					reg2= (RegistroHoja)reg.clone();
					str = this.getPalabra(reg2);
					reg2.setTerm(str);
					listaAux[i] = reg2;
				}
			}
		    // ordeno los registros con las palabras expandidas y vacio la lista original
			Arrays.sort(listaAux);
		    listaRegistroHoja.clear();
			reg = listaAux[0];
			reg.setCantCaractNuevos(reg.getTermino().length());
			reg.setCantCaractRepetidos(0);
			str=reg.getTermino();
			listaRegistroHoja.add(0, reg);		
			if (listaAux.length > 1) {
				FrontCoding fg = new FrontCoding();
				int i = 1;
				do {
					reg = listaAux[i];								                
					fg.compacto(str, reg.getTermino());				
                    // guardo el termino expandido en la variable str antes de perderlo
					str = reg.getTermino();
					reg.setCantCaractNuevos(fg.getNuevos());
					reg.setCantCaractRepetidos(fg.getAnteriores());
					reg.setTerm(fg.getTermino());
					this.listaRegistroHoja.add(i,reg);						
					i++;
				} while (i < listaAux.length);
			}			
		}
		else{
			RegistroHoja reg = new RegistroHoja();
			reg = this.buscarRegistro(palabra);
			reg.setOffset(offset);
			reg.setPesoGlobal(pesoGlobal);
		}
	}
	public boolean buscar(String terminoClave) {
		if (listaRegistroHoja.isEmpty())
			return false;
		else {
			String termino1;
			for (int i = 0; i <this.listaRegistroHoja.size(); i++){
				termino1 = this.getPalabra(this.listaRegistroHoja.get(i));
				if (termino1.equals(terminoClave))
							return true;
				}
			return false;
		}
	}
	/**
	 * @param String terminoClave
	 * @return Dado una palabra, devuelve el registro que contiene esa
	 *         palabra aplicando Font Coding; sino devuelve null
	 */
	public RegistroHoja buscarRegistro(String terminoClave) {
		if (listaRegistroHoja.isEmpty())
			return null;
		else {
			String termino1;
			if (!this.getListaRegistroTermino().isEmpty())
				for (int i = 0; i <this.listaRegistroHoja.size(); i++){
					termino1 = this.getPalabra(this.listaRegistroHoja.get(i));
						if (termino1.equals(terminoClave))
							return this.listaRegistroHoja.get(i);
				}
		}				
		return null;
	}
	public String getPalabra(RegistroHoja reg) {
		if (listaRegistroHoja.isEmpty())
			return "";
			//return reg.getTermino();
		else {
			RegistroHoja reg1 = new RegistroHoja();
			FrontCoding fg;
			String termino1 = null;
			String aux = null;
			int i = 0;
			reg1 = listaRegistroHoja.get(0);
			termino1 = reg1.getTermino();
			if (reg1.equals(reg))
				return termino1;
			else {
				for (i = 0; i < listaRegistroHoja.size() - 1; i++) {
					reg1 = listaRegistroHoja.get(i + 1);
					fg = new FrontCoding();
				 	aux = fg.descompacto(reg1.getCantCaractRepetidos(), reg1.getCantCaractNuevos(), termino1, reg1.getTermino());
				   	if (reg1.equals(reg))
						return aux;
					else 				
						termino1 = aux;
				}
			}
			return "";
			//return reg.getTermino();
		}
	}
	public void dividirHoja(HojaArbol nodoD,HojaArbol hojaAux) throws BucketException {

		
		int cantClaves = this.listaRegistroHoja.size();
		for (int i =0; i < cantClaves -1; i++) {
			RegistroHoja reg = this.listaRegistroHoja.get(i);
			if (i< cantClaves / 2) {
				hojaAux.insertar(this.getPalabra(reg),reg.getOffset(),reg.getPesoGlobal());
			}else {
				nodoD.insertar(this.getPalabra(reg),reg.getOffset(),reg.getPesoGlobal());
			}
		}
		
		nodoD.setPunteroASiguiente(this.getPunteroASiguiente());
	}

	public void setPunteroASiguiente(Integer punteroASiguiente) {
		this.punteroASiguiente = punteroASiguiente;
	}
	// este metodo devuelve el primer registro de la lista,
	// o nulo si esta vacia
	public RegistroHoja getMenorRegistro() {
		if (listaRegistroHoja.isEmpty())
			return null;
		else
			return listaRegistroHoja.get(0);
	}

	public RegistroHoja getMayorRegistro() {
		return listaRegistroHoja.get(listaRegistroHoja.size() - 1);
	}

			



}
